import { Context, Schema, h } from 'koishi'

const request = require('request');
import fs from "fs";

import Sharp from "Sharp";

import {} from 'koishi-plugin-puppeteer'

import {} from 'koishi-plugin-assets-local'

export const inject = ['puppeteer', 'database', 'assets', 'server'] // 注入插件

import schedule from 'node-schedule'

import { pathToFileURL } from 'url'
import { resolve } from 'path'

import { calc } from "a-calc";

import moment from "moment";

import { gameConfig } from "./utils/config";

import { 
  renderGoodsHTML, 
  renderItemsHTML, 
  renderEquipmentHTML, 
  renderUserLevelHTML, 
  renderUserLogHTML, 
  renderRankingHTML,
  renderTombstoneHTML,
  renderBattleLogHTML,
  renderBattleConfirmHTML,
  renderPlantInfoHTML,
} from "./utils/renderHTML";

import { getShopList } from "./utils/shop";

import { itemList } from "./utils/item";

import { everyDayTask, getUserDefaultTask, handleTaskDone, getTaskListText } from "./utils/task";

import { getTargetWeather, weatherList, getWeatherList } from "./utils/weather";

import { createBattle, battleTimerCallback } from "./utils/battle";

import { Battle, Tombstone, Task } from "./utils/type";

import { orderHelp } from "./utils/help";

import { zombieList } from "./utils/zombie";

import { userGrowPlantCallback } from "./utils/plant";

import { tombstoneOpenTip, userDefaultTombstone, getTombstoneInfo, userWakeUpZombieCallback, getUserDefaultTombstone } from "./utils/tombstone";

import { 
  getDefaultUserInfo, 
  chineseNumberToNumber, 
  simpleOrderList, 
  levelName, 
  plantOrderList, 
  plantInfoList, 
  totalPlant, 
  minuteToHM, 
  userHarvestSunCallback, 
  levelList,
  calcTargetUserSunDropAmount,
  getsSunFullNeedTimeStr,
  defaultUserLog,
  defaultUserConfig,
  UserSignInCallback,
  deletePlantCallback,
  defaultUserExtension,
  defaultUserEquipment,
  deletePlantTypeList,
  defendConfig,
} from './utils/utils'
import { send } from 'process';
import { Session } from 'inspector';

// 网络请求
const HttpRequest = async (ctx: Context, url: string, data: any) => {
  return new Promise((resolve, reject) => {
    ctx.http.post(url, data)
    .then(res => {
      resolve(res)
    })
    .catch(err => {
      reject(err)
    })
  })
}
const Request: any = {}
const urlList = {
  'getImage': ' https://api.lolicon.app/setu/v2',
  'wiki': 'https://plantsvszombies.fandom.com/wiki/Plants_vs._Zombies/Gallery',
}
for (let key in urlList) {
  Request[key] = async (ctx: Context, data: any) => {
    return await HttpRequest(ctx, urlList[key], data)
  }
}
// 结束

declare module 'koishi' { // 扩展表
  interface Tables {
    userList: userListType,
    weather: Weather,
    battle: Battle,
  }
}
interface defaultUserInfoType {
  signInTimes: number,
  targetDateIsSignIn: boolean,
  sunTotal: number,
  sun: number,
  maxSun: number,
  money: number,
  level: number,
  exp: number,
  allExp: number,
  sunDropRate: number,
  sunDropAmount: number,
  maxPlants: number,
  shopRefreshTime: number,
  shopRefreshPrice: number,
  nextSunDropTime: string,
  isChanged: boolean,
  isChangedMoney: boolean, // 临时字段 植物是否生产了金钱
  addMoney: number, // 临时字段 植物生产的金钱数量
  protectEndTime: string,
  isBattling: boolean,
}

interface plant {
  id: number,
  name: string,
  price: number,
  type: number,
  sun: number,
  maxHp: number,
  hp: number,
}

interface ItemInfo {
  name: string,
  id: number,
  desc: string,
  price: number, // 卖出价格
  isCanSell: boolean, // 是否可卖出
  isCanTransfer: boolean, // 是否可转让(无条件交易)
  isCanTrade: boolean, // 是否可交易
  isCanDiscard: boolean, // 是否可丢弃
  type: string,
  isCanUse: boolean, // 是否可使用
  alias: Array<string>, // 别名
  gift: Array<any>,
  changeType: string, // 更改的类型 data: 更改用户信息, plant: 更改用户植物信息
  callback: Function, // 使用道具后的 回调函数
  returnProbability: number, // 铲除返还阳光概率
  returnRatio: number, // 铲除返还阳光比例
  weatherUtilsAction: string, // 天气相关道具 回调函数
  value: number, // 阳光储存器 额外储存比例
  durability: number, // 阳光储存器 耐久度
}

interface Item {
  id: number,
  num: number,
  info: ItemInfo
}

interface PlantLogItem {
  time: string,
  type: string,
  desc: string,
  addPlant: Array<any>,
  reducePlant: Array<any>,
}

interface SunLogItem {
  time: string,
  num: number,
  beforeSun: number,
  afterSun: number,
  type: string,
  desc: string,
}

interface MoneyLogItem {
  time: string,
  num: number,
  beforeMoney: number,
  afterMoney: number,
  type: string,
  desc: string,
  buyItemId: number,
  buyNum: number,
}

interface UserLog {
  plantLog: Array<PlantLogItem>, // 植物相关日志
  moneyLog: Array<MoneyLogItem>, // 购买相关日志
  sunLog: Array<SunLogItem>, // 收获阳光日志
}
interface UserConfig {
  deletePlantType: any,
  defendConfig: any,
}

interface Extension {
  extensionNumber: number, // 扩建数量
  usedExtension: Array<number>, // 已购买的 不可重复购买的扩建的id
}

// 装备 类型
type Shovel = { // 铲子
  itemId: number,
  info: ItemInfo,
}
type PlantBook = { // 图鉴
  has: Boolean,
  level: number,
}
type Botany = { // 植物学
  has: Boolean,
  level: number,
}
type WateringCan = { // 水壶
  itemId: number,
  info: ItemInfo,
} 
type Fertilizer = { // 肥料
  itemId: number,
  info: ItemInfo,
}
type SunReservoir = {
  itemId: number,
  info: ItemInfo,
}
interface UserEquipment {
  shovel: Shovel,
  plantBook: PlantBook,
  botany: Botany,
  wateringCan: WateringCan,
  fertilizer: Fertilizer,
  sunReservoir: SunReservoir,
}

export interface userListType {
  name: string,
  id: string,
  plant: Array<plant>,
  data: defaultUserInfoType,
  items: Array<Item>,
  userLog: UserLog,
  userConfig: UserConfig,
  extension: Extension,
  equipment: UserEquipment,
  tombstone: Tombstone,
  task: Task
}

export interface Weather {
  id: number,
  name: string,
  desc: string,
  info: string,
  time: string,
  sunDropAmountBuff: number,
  sunDropRateBuff: number,
  unLockTime: string,
}

export const name = 'pvz'

export interface Config {}

export const Config: Schema<Config> = Schema.object({})

export function apply(ctx: Context) {

  ctx.model.extend('userList', { // 扩展表
    id: {
      type: 'string',
      nullable: false,
    },
    name: {
      type: 'string',
      nullable: false,
    },
    plant: {
      type: 'array',
      nullable: false,
    },
    data: {
      type: 'object',
      nullable: false,
    },
    items: {
      type: 'array',
      nullable: false,
    },
    userLog: {
      type: 'object',
      nullable: false,
    },
    userConfig: {
      type: 'object',
      nullable: false,
    },
    extension: {
      type: 'object',
      nullable: false,
    },
    equipment: {
      type: 'object',
      nullable: false,
    },
    tombstone: {
      type: 'object',
      nullable: false,
    },
    task: {
      type: 'object',
      nullable: false,
      initial: getUserDefaultTask()
    },
  })
  ctx.model.extend('weather', {
    id: {
      type: 'unsigned',
      nullable: false,
    },
    name: {
      type: 'string',
      nullable: false,
    },
    desc: {
      type: 'string',
      nullable: false,
    },
    info: {
      type: 'string',
      nullable: false,
    },
    time: {
      type: 'string',
      nullable: false,
    },
    sunDropAmountBuff: {
      type: 'float',
      nullable: false,
    },
    sunDropRateBuff: {
      type: 'float',
      nullable: false,
    },
    unLockTime: {
      type: 'string',
      nullable: false,
    },
  })
  ctx.model.extend('battle', {
    id: {
      type: 'string',
      nullable: false,
    },
    atk: {
      type: 'object',
      nullable: false,
    },
    def: {
      type: 'object',
      nullable: false,
    },
    start: {
      type: 'string',
      nullable: false,
    },
    end: {
      type: 'string',
      nullable: false,
    },
    timeLine: {
      type: 'array',
      nullable: false,
    },
    log: {
      type: 'array',
      nullable: false,
    },
    lostPlant: {
      type: 'array',
      nullable: false,
    },
    lostSun: {
      type: 'unsigned',
      nullable: false,
    },
    result: {
      type: 'string',
      nullable: false,
    },
    simulation: {
      type: 'unsigned',
      nullable: false,
    },
    battleDuration: {
      type: 'string',
      nullable: false,
      initial: '', // 默认值
    },
  })
  console.log('表扩展完毕');

  const sendGuildMessage = async (text) => {
    let bot = ctx.bots.find(bot => bot.platform === 'qqguild')
    if (!bot) return
    else {
      let Guild = await (await bot.getGuildList()).data[0].id
      // let MemberList = await (await bot.getGuildMemberList(Guild)).data[1]
      // 10761656720966972998
      // let ChannelId = await (await bot.getChannelList(Guild)).data.find(channel => channel.name === '活动').id
      // console.log('ChannelId',ChannelId)
      // bot.sendMessage(ChannelId, text).then(r => console.log('发送成功', r)).catch(e => console.log('发送失败', e))
    }
  }

  ctx.command('入驻 <name>').action(async (_, name = '') => {

    const { session } = _
    let { event } = session
    let { user } = event
    let res = await autoRegister(name, user.id)
    return res

  }).alias('入驻花园') // 指令别名 说或入驻花园均可触发

  // 用户注册
  const autoRegister = async (name: string, userId: any) => { 
    
    name = String(name).trim()

    if (!name) return '请输入用户名！'

    let hasUser = await ctx.database.get('userList', {
      name: [name],
      id: [userId]
    })

    let result = ''
    if (hasUser.length > 0) result = '你已经入驻了花园'
    else {
      let registerUser = {
        id: userId,
        name: name,
        plant: [],
        items: [],
        userLog: defaultUserLog,
        userConfig: defaultUserConfig,
        extension: defaultUserExtension,
        equipment: defaultUserEquipment,
        tombstone: getUserDefaultTombstone(),
        task: getUserDefaultTask(),
        data: getDefaultUserInfo(),
      }
      await ctx.database.create('userList', registerUser)
      .then(res => {
        console.log('已初始化用户', res);
        result = name + `，你获得了属于你的一片土地，开始你的种植之旅吧！`
      })
      .catch(err => {
        console.log('初始化用户失败', err);
        result = '入驻办理手续好像出了点问题，或许你可以联系一下工作人员'
      })
    }

    return result

  }

  const checkRegister = async (session: any) => { // 检测是否注册 如未注册则等待注册完成返回
    let { event } = session
    let { user } = event
    let hasUser = await ctx.database.get('userList', [user.id])
    if (hasUser.length > 0) return
    else {
      await autoRegister(user.name, user.id)
      return
    }
  }

  // 删除所有用户
  const deleteAllUser = async () => {
    ctx.database.remove('userList', {})
  }

  // 重置当前用户数据
  const resetUser = async (userId: string) => {
    await ctx.database.set('userList', [userId], {
      plant: [],
      items: [],
      userLog: defaultUserLog,
      userConfig: defaultUserConfig,
      extension: defaultUserExtension,
      equipment: defaultUserEquipment,
      tombstone: getUserDefaultTombstone(),
      task: getUserDefaultTask(),
      data: getDefaultUserInfo(),
    })
  }

  // 补充数据库中所有用户的缺失字段 已有的字段不作覆盖
  const initAllUserDataBase = async (userId: string) => {
    // let userData 
    // let resultData = 
  }

  // 作弊
  const cheat = async (order: string, session: any) => {
    let { event } = session
    let { user } = event
    // 当前用户
    let hasUser = await ctx.database.get('userList', user.id)
    let targetUser = hasUser[0]
    let data = targetUser.data
    if (order == '1') {
      data.sunTotal = data.sunTotal + 5000
    }
    else if (order == '2') {
      data.money = data.money + 5000
    }
    await ctx.database.set('userList', [user.id], {
      data
    })
  }


  // 我的植物
  const myPlant = async (session: any) => {
    let { event } = session
    let { user } = event
    // 当前用户
    let hasUser = await ctx.database.get('userList', user.id)
    let targetUser = hasUser[0]
    let plant = targetUser.plant
    let result = totalPlant(plant, targetUser.data, targetUser.extension.extensionNumber)
    if (plant.length == 0) return session.send(result)
    else render(ctx, result).then(img => {
      return session.send(img)
    })
  }

  const userInfo = async (session: any) => {
    let { event } = session
    let { user } = event
    await checkRegister(session)
    let hasUser = await ctx.database.get('userList', user.id)
    let weather = await ctx.database.get('weather', {})
    let targetWeather = weather[0]
    let targetUser = hasUser[0]
    let levelIndex = targetUser.data.level - 1
    let maxSun = targetUser.data.maxSun
    if (targetUser.equipment.sunReservoir.itemId) maxSun = maxSun * targetUser.equipment.sunReservoir.info.value
    let hasSunReservoir = targetUser.equipment.sunReservoir.itemId ? `\n(已装备阳光储存器，收取阳光将消耗耐久)` : ''
    let hasSunMax = targetUser.data.sun >= maxSun // 用户阳光达到上限
    let nextSunDropTime = hasSunMax ? '阳光已经装不下了，当前生产已停止' : targetUser.data.nextSunDropTime
    let sunFullNeedTimeStr = getsSunFullNeedTimeStr(targetUser.data, targetWeather, targetUser.equipment)
    let needExp = levelList[levelIndex].exp - targetUser.data.exp
    let signTip = !targetUser.data.targetDateIsSignIn ? `\n今天你还没签到哦，记得签到喵~` : ''
    let weatherTip = `\n当前天气：` + targetWeather.name
    let extensionNumber = targetUser.extension.extensionNumber
    let plantLimit = `\n植物上限：${targetUser.data.maxPlants}`
    if (extensionNumber) plantLimit = plantLimit += ` (花园扩建+${extensionNumber})`
    let userSunDropAmount = '' // 阳光产出量
    let userSunDropRate = '' // 阳光产出频率
    // 天气buff
    if (targetWeather.sunDropAmountBuff != 1) {
      targetUser.data.sunDropAmount = Math.floor(targetUser.data.sunDropAmount * targetWeather.sunDropAmountBuff)
      userSunDropAmount = targetUser.data.sunDropAmount + '（已受天气影响）'
    }
    else userSunDropAmount = targetUser.data.sunDropAmount.toString()
    if (targetWeather.sunDropRateBuff != 1) {
      targetUser.data.sunDropRate = calc(`${targetUser.data.sunDropRate} * (1 / ${targetWeather.sunDropRateBuff}) | <=2`) as any
      userSunDropRate = targetUser.data.sunDropRate + '分钟（已受天气影响）'
    }
    else userSunDropRate = targetUser.data.sunDropRate + '分钟'
    let protectEndTimeTip = targetUser.data.protectEndTime && moment().isBefore(moment(targetUser.data.protectEndTime, 'YYYY-MM-DD HH:mm:ss')) ? '\n庇护结束时间：' + targetUser.data.protectEndTime : '\n当前不处于庇护中'
    let isBattlingTip = targetUser.data.isBattling ? '\nTip：花园正在被入侵，无法进行种植与铲除操作' : ''
    let result = `${targetUser.name}的花园\n阳光池：${targetUser.data.sunTotal}\n可收获阳光：${targetUser.data.sun} / ${maxSun}${hasSunReservoir}\n当前等级：${targetUser.data.level}（${levelList[levelIndex].name}）\n持有金币：${targetUser.data.money}\n阳光产出频率：${userSunDropRate}\n阳光产出量：${userSunDropAmount}${plantLimit}\n距离升级还需经验：${needExp}\n下次收获阳光时间：\n${nextSunDropTime}${sunFullNeedTimeStr}${signTip}${protectEndTimeTip}${weatherTip}${isBattlingTip}`
    render(ctx, result).then(img => {
      if (hasSunMax) {
        // 添加收取阳光按钮
        let button = `<at id="${targetUser.id}"/>`
        img = button + img
      }
      return session.send(img)
    })
    return
  }

  const harvestSun = async (session: any) => {
    let { event } = session
    let { user } = event
    await checkRegister(session)
    let hasUser = await ctx.database.get('userList', user.id)
    let targetUser = hasUser[0]
    if (targetUser.data.sun) {
      let text = `把掉落的阳光收集起来了!`
      let maxSun = targetUser.data.maxSun
      if (targetUser.equipment.sunReservoir.itemId) { // 存在阳光储存器
        maxSun = maxSun * targetUser.equipment.sunReservoir.info.value 
        targetUser.equipment.sunReservoir.info.durability-- // 耐久度减少
        text += `\n阳光储存器耐久度：${targetUser.equipment.sunReservoir.info.durability}`
        if (targetUser.equipment.sunReservoir.info.durability <= 0) { // 耐久度归零
          // 移除该道具
          targetUser.items = targetUser.items.filter(item => item.id != targetUser.equipment.sunReservoir.itemId)
          await ctx.database.set('userList', [user.id], {
            items: targetUser.items
          })
          // 重置装备
          targetUser.equipment.sunReservoir.info = {} as any
          targetUser.equipment.sunReservoir.itemId = 0
        }
        await ctx.database.set('userList', [user.id], {
          equipment: targetUser.equipment
        }) 
      }
      if (targetUser.data.sun == maxSun) { // 当前阳光生产已停止 如收取阳光 则重置下次收获阳光时间
        targetUser.data.nextSunDropTime = moment().add(targetUser.data.sunDropRate, 'minute').format('YYYY-MM-DD HH:mm')
      }
      let harvestNumber = targetUser.data.sun
      targetUser.data.sunTotal += harvestNumber
      targetUser.data.sunTotal = parseInt(targetUser.data.sunTotal.toString()) // 整数
      targetUser.data.sun = 0
      text += `\n当前阳光池：${targetUser.data.sunTotal}`
      await ctx.database.set('userList', [user.id], {
        data: targetUser.data
      })
      await saveLog(ctx, targetUser.userLog, user.id, 'sunLog', {
        time: moment().format('YYYY-MM-DD HH:mm:ss'),
        num: harvestNumber,
        beforeSun: targetUser.data.sunTotal - harvestNumber,
        afterSun: targetUser.data.sunTotal,
        type: 'harvest',
        desc: '收取阳光',
      })
      await handleUserTaskDone(user.id, 'everyDayTask', 1) // 完成每日任务 收集阳光
      let url = pathToFileURL(resolve(__dirname, `img/info/sun.gif`)).href
      let res = `
        <message>
          <image src="${url}"/>
        </message>
        <message>${text}</message>
      `
      return await session.send(res)
    }
    else {
      let url = pathToFileURL(resolve(__dirname, `img/info/notSun.jpg`)).href
      let text = '还没有可收集的阳光，再等等吧~'
      let res = `
        <message>
          <img width="100" height="100" src="${url}"/>
        </message>
        <message>${text}</message>
      `
      return await session.send(res)
    } 
  }

  const showShop = async (session: any, showAll: boolean = false) => {
    let { event } = session
    let { user } = event
    await checkRegister(session)
    let hasUser = await ctx.database.get('userList', user.id)
    let targetUser = hasUser[0]
    let goods = getShopList(targetUser.data, targetUser.items, targetUser.extension, showAll)
    await renderShop(ctx, goods, targetUser.data, showAll, targetUser.userLog).then(img => {
      return session.send(img)
    })
  }

  const signIn = async (session: any) => {
    let { event } = session
    let { user } = event
    await checkRegister(session)
    let hasUser = await ctx.database.get('userList', user.id)
    let targetUser = hasUser[0]
    if (targetUser.data.targetDateIsSignIn) {
      return session.send('今天已经签到过了喵~')
    }
    else {
      let signInResult = UserSignInCallback(targetUser.data, targetUser.items)
      await ctx.database.set('userList', [user.id], {
        data: signInResult.userData,
        items: signInResult.items
      })
      await render(ctx, signInResult.msg).then(img => {
        return session.send(img)
      })
    }
  }

  const showItems = async (session: any) => {
    let { event } = session
    let { user } = event
    await checkRegister(session)
    let hasUser = await ctx.database.get('userList', user.id)
    let targetUser = hasUser[0]
    if (targetUser.items.length == 0) return session.send('你的背包空空如也~')
    else await renderUserItems(ctx, targetUser.items, targetUser.data).then(img => {
      return session.send(img)
    })
  }

  const showEquipment = async (session: any) => {
    let { event } = session
    let { user } = event
    await checkRegister(session)
    let hasUser = await ctx.database.get('userList', user.id)
    let targetUser = hasUser[0]
    await renderUserEquipment(ctx, targetUser.equipment).then(img => {
      return session.send(img)
    })
  }

  const showLevel = async (session: any) => {
    let { event } = session
    let { user } = event
    await checkRegister(session)
    let hasUser = await ctx.database.get('userList', user.id)
    let targetUser = hasUser[0]
    await renderUserLevel(ctx, targetUser.data).then(img => {
      return session.send(img)
    })
  }

  const showLog = async (session: any, type: 'plantLog'| 'moneyLog' | 'sunLog' | 'all') => {
    let { event } = session
    let { user } = event
    await checkRegister(session)
    let hasUser = await ctx.database.get('userList', user.id)
    let targetUser = hasUser[0]
    if (type == 'all') {
      let text = '请输入需要查看的日志：\n1. 植物日志\n2. 商店日志\n3. 阳光日志'
      await render(ctx, text).then(img => {
        session.send(img)
      })
      const order = await session.prompt()
      if (order == '1') showLog(session, 'plantLog')
      else if (order == '2') showLog(session, 'moneyLog')
      else if (order == '3') showLog(session, 'sunLog')
      else return
    }
    else if (targetUser.userLog[type].length == 0) return session.send('暂无相关记录喵~')
    else await renderUserLog(ctx, targetUser.userLog, type).then(img => {
      return session.send(img)
    })
  }

  const showBattleLog = async (session: any, page = 1) => {
    page = page || 1
    let { event } = session
    let { user } = event
    await checkRegister(session)
    let list = await ctx.database.get('battle', {})
    list = list.filter(i => {
      return i.def.id == user.id
    })
    list = list.map((i: any, index) => {
      i.index = index
      return i
    })
    list = list.sort((a, b) => {
      return moment(b.start, 'YYYY-MM-DD HH:mm:ss').unix() - moment(a.start, 'YYYY-MM-DD HH:mm:ss').unix()
    })
    list = list.slice((page - 1) * 5, page * 5) // 一页5条
    if (list.length == 0) return await session.send('暂无相关记录喵~')
    else {
      await renderBattleLog(ctx, list).then(img => {
        return session.send(img)
      })
    }
  }

  const showBattleInfo = async (session: any, id: number) => {
    let { event } = session
    let { user } = event
    await checkRegister(session)
    let list = await ctx.database.get('battle', {})
    list = list.filter(i => {
      return i.def.id == user.id
    })
    list = list.map((i: any, index) => {
      i.index = index
      return i
    })
    list = list.sort((a, b) => {
      return moment(b.start, 'YYYY-MM-DD HH:mm:ss').unix() - moment(a.start, 'YYYY-MM-DD HH:mm:ss').unix()
    })
    if (list.length == 0) return await session.send('暂无相关记录喵~')
    else {
      let targetBattle = list.find((i: any) => i.index == id)
      if (!targetBattle) return await session.send('没有匹配的对局喵~')
      else {
        await render(ctx, targetBattle.result).then(img => {
          session.send(img)
        })
        // 战斗过程过长则省略部分
        let text = targetBattle.timeLine.length < 360 ? targetBattle.timeLine.join('\n') : targetBattle.log.join('\n')
        await render(ctx, text, true).then((img) => {
          session.send(img)
        })
      }
    }
  }

  const resetLog = async (session: any) => {
    let { event } = session
    let { user } = event
    await checkRegister(session)
    let text = '确认清空日志吗？确认请输入yes'
    await session.send(text)
    const isOK = await session.prompt()
    if (isOK == 'yes') {
      await ctx.database.set('userList', [user.id], {
        userLog: defaultUserLog
      })
      return session.send('日志已经被我吃光光啦喵~')
    }
  }

  const showWeather = async (session: any) => {
    let weather = await ctx.database.get('weather', {})
    let targetWeather = weather[0]
    let text = `当前天气：${targetWeather.name}\n天气效果：${targetWeather.info}\n${targetWeather.desc}\n更新时间：${moment(targetWeather.time, 'YYYY-MM-DD HH:mm:ss').format('HH:mm:ss')}\n天气每小时更新一次`
    render(ctx, text).then(img => {
      return session.send(img)
    })
  }

  const showUserStrategy = async (session: any) => {
    let { event } = session
    let { user } = event
    await checkRegister(session)
    let hasUser = await ctx.database.get('userList', user.id)
    let targetUser = hasUser[0]
    let userConfig = targetUser.userConfig
    let res = [
      '用户风格偏好设置',
      `「 delete 」铲除植物判断：${deletePlantTypeList[userConfig.deletePlantType]}`,
      '可选值：',
      ' · auto： 按种植时间先后铲除',
      ' · hp： 优先铲除当前血量较低的植物',
      ' · level： 优先铲除等级较低的植物',
      '',
      `「 defend 」防守决策：${defendConfig[userConfig.defendConfig]}`,
      '可选值：',
      ' · auto： 自动',
      ' · distance： 优先攻击最近的目标',
      ' · hph： 优先攻击血量较高的目标',
      ' · hpl： 优先攻击血量较低的目标',
      ' · not： 不进行防守',
      '如需修改，请输入相应的策略名称喵~',
    ] as any
    res = res.join('\n')
    render(ctx, res).then(img => {
      return session.send(img)
    })
    const order = await session.prompt()
    if (order == 'delete') {
      let text = '请选择铲除植物判断依据：\nauto：按种植时间先后铲除\nhp：优先铲除当前血量较低的植物\nlevel：优先铲除等级较低的植物'
      await render(ctx, text).then(img => {
        return session.send(img)
      })
      const order = await session.prompt()
      if (order == 'auto' || order == 'hp' || order == 'level') {
        userConfig.deletePlantType = order
        await ctx.database.set('userList', [user.id], {
          userConfig: userConfig
        })
        return session.send('已保存喵~')
      }
    }
    else if (order == 'defend') {
      let text = '请选择防守决策：\nauto：自动\ndistance：优先攻击最近的目标\nhph：优先攻击血量较高的目标\nhpl：优先攻击血量较低的目标\nnot：不进行防守'
      await render(ctx, text).then(img => {
        return session.send(img)
      })
      const order = await session.prompt()
      if (order == 'auto' || order == 'distance' || order == 'hph' || order == 'hpl' || order == 'not') {
        userConfig.defendConfig = order
        await ctx.database.set('userList', [user.id], {
          userConfig: userConfig
        })
        return session.send('已保存喵~')
      }
    }
  }

  // 更新当前天气
  const changeWeather = async (isSend = true, isChange = false, selectWeatherId = false, isLock = false) => { 
    // isSend 主动推送群信息 isChange 非自动更新状态 selectWeatherId 指定更改为目标天气 锁定当前天气时间 单位小时
    // isNatural 是否自然更新天气
    let weather = await ctx.database.get('weather', {})
    if (weather[0].unLockTime != 'none') {
      let isUnLock = moment().isBefore(moment(weather[0].unLockTime, 'YYYY/MM/DD HH:mm:ss'))
      if (isUnLock) {
        // if (!isChange) return sendGuildMessage('天启石生效中，当前天气不变喵~')
      }
    }
    let targetWeather = getTargetWeather(selectWeatherId)
    targetWeather.time = moment().format('YYYY/MM/DD HH:00:00')
    // 重置当前天气
    await ctx.database.remove('weather', {})
    // 更新
    delete targetWeather.day
    delete targetWeather.night
    if (isLock) targetWeather.unLockTime = moment().add(isLock as any, 'hours').format('YYYY/MM/DD HH:mm:ss')
    else targetWeather.unLockTime = 'none'
    await ctx.database.create('weather', targetWeather)
    if (isSend) {
      if (isChange) targetWeather.time = moment().format('YYYY/MM/DD HH:mm:ss')
      let text = `当前天气：${targetWeather.name}\n天气效果：${targetWeather.info}\n${targetWeather.desc}\n更新时间：${moment(targetWeather.time, 'YYYY-MM-DD HH:mm:ss').format('HH:mm:ss')}\n天气每小时更新一次`
      render(ctx, text).then(img => {
        // return sendGuildMessage(img)
      })
    }
  }

  const weatherUtilsActionList = {
    // 使用避风护符的效果
    changeWeather: async (session: any) => {
      session.send(`使用成功，当前天气状态已重置喵~`)
      await changeWeather(true, true)
      return true // 使用成功
    },
    selectWeatherAndLock: async (session: any) => {
      // weatherList
      let text = `请选择需要变更后的天气喵：`
      getWeatherList().map(i => {
        text += `\n · ${i.id}、${i.name}`
      })
      session.send(text)
      const id = await session.prompt()
      if (id == undefined) return false
      let targetWeather = getWeatherList().find(i => i.id == id)
      if (targetWeather) {
        await changeWeather(true, true, targetWeather.id, 12 as any)
        session.send('使用成功，当前天气已变更喵~')
      }
      else {
        session.send('使用失败，请输入正确的天气id喵~')
        return false
      }
    },
  }

  // 模拟对战
  const fakeBattle = async (session: any, isAt = false) => {
    await checkRegister(session)
    let lv = 0
    if (isAt) lv = 1
    else {
      await session.send('请输入难度喵：\n难度选择范围1 ~ 10喵~')
      lv = await session.prompt()
      if (lv == undefined) return
      let isNumber = /^[0-9]+$/.test(lv as any)
      if (!isNumber || lv < 1 || lv > 10) return session.send('请输入1 ~ 10的数字喵喵~')
    }
    let { user } = session.event
    let hasUser = await ctx.database.get('userList', user.id)
    let targetUser = hasUser[0]
    if (targetUser.data.isBattling) return session.send('花园正在被入侵中喵~\n等等再来吧')
    let { defendConfig } = targetUser.userConfig
    let atkInfo = {
      name: '游荡的僵尸',
      id: '0'
    }
    let defInfo = {
      name: targetUser.name,
      id: targetUser.id,
      plant: targetUser.plant
    }
    session.send('一大波僵尸正在逼近喵~')
    targetUser.data.isBattling = true
    await ctx.database.set('userList', user.id, {
      data: targetUser.data
    })
    handleBattle(atkInfo, defInfo, true, lv, [], {
      session,
      showGif: true,
      sunTotal: targetUser.data.sunTotal
    })
    .then((res: any) => {
      targetUser.data.isBattling = false
      ctx.database.set('userList', user.id, {
        data: targetUser.data
      })
      render(ctx, res.result).then(img => {
        // sendGuildMessage(img)
        session.send(img)
      })
      // 战斗过程过长则省略部分
      let text = session.platform == 'kook' || res.timeLine.length < 360 ? res.timeLine.join('\n') : res.log.join('\n')
      render(ctx, text, true).then((img) => {
        // sendGuildMessage(img)
        session.send(img)
      })
    })
  }

  // 生成对战
  // atkInfo atkInfo.name atkInfo.id atkInfo.plant
  // defInfo defInfo.name defInfo.id
  // simulation 是否为模拟对战
  // lv 尸潮难度 不传默认为1
  // atkZombieList 指定僵尸
  // data 额外参数 立即发送僵尸的gif时传
  // lv: 模拟对战传 atkZombieList: 玩家发动传
  const handleBattle = async (atkInfo, defInfo, simulation = false, lv: any, atkZombieList = [], data = {} as any) => {
    return new Promise((resolveCallback) => {
      lv = lv || 1
      let info = createBattle(defInfo.plant, lv, atkZombieList)
      if (data.showGif) {
        let leaderIndex = info.atk.zombieList.reduce((pre: number, item: any, index: number) => {
          return info.atk.zombieList[pre].dangerLevel < info.atk.zombieList[index].dangerLevel ? index : pre
        }, 0)
        let leader = info.atk.zombieList[leaderIndex]
        let url = pathToFileURL(resolve(__dirname, `img/zombie/${leader.img}.gif`) as any).href
        data.session.send(h.image(url))
      }
      let beforeBattlePlantList = [...info.def.plantList] // 记录战斗前的植物列表
      info.id = new Date().getTime()
      info.atk.name = atkInfo.name
      info.atk.id = atkInfo.id
      info.def.name = defInfo.name
      info.def.id = defInfo.id.toString()
      info.start = moment().format("YYYY-MM-DD HH:mm:ss")
      if (data.sunTotal != undefined) info.sunTotal = data.sunTotal // 被攻击花园的当前阳光数
      let targetTime = 0 // 当前时间 单位毫秒
      let timer = setInterval(async() => {
        targetTime += 100
        let res = battleTimerCallback(info, targetTime)
        info = res.info
        if (res.isEnd || targetTime > 300000) {
          clearInterval(timer)
          timer = null
          if (simulation) return resolveCallback(info) // 模拟对战 不进行数据库操作
          // 战斗后存活植物
          let userPlantList = beforeBattlePlantList.filter(i => info.def.plantList.find((j: any) => j.battleId == i.battleId)).map(i => {
            delete i.battleId
            delete i.x
            delete i.y
            delete i.atkInterval
            if (i.num != undefined) delete i.num
            return i
          })
          let setParams = {
            plant: userPlantList,
          } as any
          // 防守方奖励结算
          let defRewardItems = info.reward.def.items
          if (defRewardItems && defRewardItems.length) {
            let defUserInfo = await ctx.database.get('userList', info.def.id)
            let defUserInfoItems = defUserInfo[0].items
            defRewardItems.forEach(i => {
              let hasItemIndex = defUserInfoItems.findIndex((j: any) => j.id == i.itemId)
              if (hasItemIndex > -1) defUserInfoItems[hasItemIndex].num += i.num
              else {
                let newItemInfo = itemList.find((j: any) => j.id == i.itemId)
                defUserInfoItems.push({
                  id: i.itemId,
                  num: 1,
                  info: newItemInfo
                })
              }
            })
            setParams.items = defUserInfoItems
          }
          await ctx.database.set('userList', info.def.id, setParams)
          // 用户发起的攻击
          if (data.isUserAtk) { 
            // 攻击方奖励结算
            let setParams1 = {} as any
            let atkUserInfo = await ctx.database.get('userList', info.atk.id)
            setParams1.tombstone = atkUserInfo[0].tombstone
            setParams1.tombstone.zombie = info.isComingZombie // 存活僵尸 归队
            let atkRewardItems = info.reward.atk
            if (atkRewardItems.undeadEssence) { // 奖励包含不死精元
              setParams1.tombstone.undeadEssence += atkRewardItems.undeadEssence
            }
            if (atkRewardItems.items && atkRewardItems.items.length) { // 奖励包含道具
              let atkUserInfoItems = atkUserInfo[0].items
              atkRewardItems.items.forEach(i => {
                let hasItemIndex = atkUserInfoItems.findIndex((j: any) => j.id == i.itemId)
                if (hasItemIndex > -1) atkUserInfoItems[hasItemIndex].num += i.num
                else {
                  let newItemInfo = itemList.find((j: any) => j.id == i.itemId)
                  atkUserInfoItems.push({
                    id: i.itemId,
                    num: 1,
                    info: newItemInfo
                  })
                }
              })
              setParams1.items = atkUserInfoItems
            }
            await ctx.database.set('userList', info.atk.id, setParams1)
            info.result += '\n' + info.reward.atk.str
          }
          info.userPlantList = userPlantList
          const battleData = { // 存入数据库
            id: info.id,
            atk: info.atk,
            def: info.def,
            start: info.start,
            end: info.end,
            timeLine: info.timeLine,
            log: info.log,
            lostPlant: info.lostPlant,
            lostSun: info.lostSun,
            result: info.result,
            simulation: simulation ? 1 : 0,
            battleDuration: info.battleDuration,
          }
          ctx.database.create('battle', battleData)
          resolveCallback(info)
        }
      }, 10)
    })
  }

  // 显示花园帮助
  const showHelp = async (session: any) => {
    return session.send('尚未完善')
  }

  // 显示排行榜
  const showRanking = async (session: any) => {
    await session.send('请输入想要查看的排行榜喵~\n1. 阳光池\n2. 花园等级\n3. 可用金钱')
    let type = await session.prompt()
    if (type == '1' || type == '2' || type == '3') {
      let userList = await ctx.database.get('userList', {})
      let key = ''
      if (type == '1') key = 'sunTotal'
      else if (type == '2') key = 'level'
      else if (type == '3') key = 'money'
      let list = userList.map(i => {
        let item = {
          name: i.name,
          value: null
        }
        item.value = i.data[key]
        return item
      })
      list.sort((a, b) => {
        return b[key] - a[key]
      })
      return await renderRanking(ctx, list).then(img => {
        return session.send(img)
      })
    }
    else return
  }

  // 显示指令帮助
  const showOrderHelp = async (session: any) => {
    let text = orderHelp.join('\n')
    render(ctx, text).then(img => {
      return session.send(img)
    })
  }

  // 显示墓碑面板
  const showTombstone = async (session: any) => {
    await checkRegister(session)
    let { event } = session
    let { user } = event
    let hasUser = await ctx.database.get('userList', user.id)
    let targetUser = hasUser[0]
    if (!targetUser.tombstone.isUnlock) {
      let hasKey = targetUser.items.find((i: Item) => i.info.id == 29)
      if (hasKey) {
        targetUser.tombstone.isUnlock = true // 拥有钥匙 解锁墓碑
        await ctx.database.set('userList', user.id, {
          tombstone: targetUser.tombstone
        })
      }
      else return session.send('未持有安息乡的钥匙喵\n请先去商店购买以解锁更多功能喵~')
    }
    let { cdEnd, wakeUpTime } = targetUser.tombstone
    let isOk = true
    if (moment().isAfter(moment(cdEnd, 'YYYY-MM-DD HH:mm:ss'))) {
      isOk = false
    }
    else if (moment().isAfter(moment(wakeUpTime, 'YYYY-MM-DD HH:mm:ss'))) {
      isOk = false
    }
    if (!isOk) await ctx.database.set('userList', user.id, { // 修复错误数据
      tombstone: userWakeUpZombieCallback(targetUser.tombstone) 
    })
    return await render(ctx, getTombstoneInfo(targetUser.tombstone)).then(img => {
      return session.send(img)
    })
  }

  // 唤醒僵尸
  const wakeUpZombie = async (session: any, isAt = false) => {
    if (isAt) return
    let { event } = session
    let { user } = event
    let hasUser = await ctx.database.get('userList', user.id)
    let targetUser = hasUser[0]
    if (targetUser.tombstone.isUnlock) {
      let res = targetUser.tombstone
      let { level, zombie, zombieMax, wakeUpZombieMax, wakeUpTime, cdEnd } = res
      if (wakeUpTime && moment().isBefore(moment(wakeUpTime, 'YYYY-MM-DD HH:mm:ss'))) await render(ctx, '当前还在唤醒中喵~\n唤醒完成时间：\n' + wakeUpTime).then(img => {
        return session.send(img)
      })
      else if (cdEnd && moment().isBefore(moment(cdEnd, 'YYYY-MM-DD HH:mm:ss'))) await render(ctx, '墓碑还在冷却中喵~\n冷却完成时间：\n' + cdEnd).then(img => {
        return session.send(img)
      })
      else {
        let text = `请选择需要唤醒的僵尸的数量喵~\n当前最多可同时唤醒${wakeUpZombieMax}个僵尸\n注意：\n一次唤醒的僵尸越多，需要的时间与墓碑的冷却时间都会越长\nTip：当唤醒后的僵尸数量已经超过了墓园的上限\n则超过部分僵尸会变为游荡僵尸...`
        await render(ctx, text).then(img => {
          session.send(img)
        })
        let num = await session.prompt()
        if (num == undefined) return
        num = Number(num)
        if (num <= 0 || num > wakeUpZombieMax) return session.send('哎呀，请输入正确的数量喵~')
        let list = zombieList.filter(item => item.tombstoneLevel <= level)
        let wakeUpZombie = []
        for (let index = 0; index < num; index++) {
          let targetZombie = list[Math.floor(Math.random() * list.length)]
          wakeUpZombie.push(targetZombie)
        }
        if (zombie.length + wakeUpZombie.length > zombieMax) {
          let excrescentZombieNum = wakeUpZombie.length - (zombieMax - zombie.length)
          let excrescentZombie = wakeUpZombie.splice(0, excrescentZombieNum)
          await session.send('你唤醒的僵尸数量已经超过了墓园的上限...\n超出部分已转化为游荡僵尸喵~')
          TimerFotZombieGenerate('excrescentZombie', {
            session,
            excrescentZombie
          })
          // console.log('游荡僵尸', excrescentZombie);
        }
        if (wakeUpZombie.length) {
          let resWakeUpZombie = []
          wakeUpZombie.map(i => {
            let hasIndex = resWakeUpZombie.findIndex(item => item.id == i.id)
            if (hasIndex == -1) {
              i.num = 1
              resWakeUpZombie.push(i)
            }
            else resWakeUpZombie[hasIndex].num += 1
          })
          let leaderIndex = wakeUpZombie.reduce((pre, item, index) => {
            return wakeUpZombie[pre].dangerLevel < wakeUpZombie[index].dangerLevel ? index : pre
          }, 0)
          let leader = wakeUpZombie[leaderIndex]
          targetUser.tombstone.wakeUpZombie = [...resWakeUpZombie] // 唤醒中的僵尸
          let wakeUpNeedTime = wakeUpZombie.length * gameConfig.wakeUpCdTime // 唤醒需要的时间 = 唤醒僵尸数量 * 5 分钟 墓碑冷却结束时间为其两倍
          targetUser.tombstone.wakeUpTime = moment().add(wakeUpNeedTime, 'minutes').format('YYYY-MM-DD HH:mm:ss') // 唤醒完成时间
          targetUser.tombstone.cdEnd = moment().add(wakeUpNeedTime * 2, 'minutes').format('YYYY-MM-DD HH:mm:ss') // 墓碑冷却结束时间
          await ctx.database.set('userList', user.id, {
            tombstone: targetUser.tombstone
          })
          let url = pathToFileURL(resolve(__dirname, `img/zombie/${leader.img}.gif`)).href
          await session.send(h.image(url))
          let okText = '你感受到了来自地下的躁动\n僵尸们破土而出....\n距离完全唤醒还需要' + wakeUpNeedTime + '分钟喵~\n'
          okText += '本次唤醒的僵尸列表：\n'
          resWakeUpZombie.map(item => {
            okText += `${item.name} x ${item.num}\n`
          })
          okText += targetUser.tombstone.wakeUpTime + '后，本次唤醒的僵尸们即将进入可用僵尸队列中\n'
          okText += '墓碑已经入冷却，墓碑冷却完成时间：' + targetUser.tombstone.cdEnd + '喵~'
          await render(ctx, okText).then(img => {
            return session.send(img)
          })
          await handleUserTaskDone(user.id, 'everyDayTask', 7) // 完成每日任务 唤醒一次僵尸
        }
        else return
      }
    }
    else return session.send('未持有安息乡的钥匙喵\n请先去商店购买以解锁更多功能喵~')
  }

  // 攻击玩家
  const atkUser = async (session: any, atkUserId: string | number, defUserId: string | number) => {
    if (atkUserId == defUserId) return session.send('不能攻击自己的花园喵~')
    let atkUser = await ctx.database.get('userList', atkUserId)
    let defUser = await ctx.database.get('userList', defUserId)
    let atkInfo = {
      name: atkUser[0].name,
      id: atkUser[0].id,
    }
    let defInfo = {
      name: defUser[0].name,
      id: defUser[0].id,
      plant: defUser[0].plant
    }
    let data = {
      showGif: false,
      session,
      sunTotal: defUser[0].data.sunTotal,
      isUserAtk: true,
    }
    if (atkUser[0].tombstone.isUnlock) {
      let atkZombieList = atkUser[0].tombstone.zombie
      if (atkZombieList.length) {
        if (defUser[0].data.isBattling) return session.send('对方的花园正在被入侵喵~')
        else if (defUser[0].data.protectEndTime && moment(defUser[0].data.protectEndTime, 'YYYY-MM-DD HH:mm:ss').isAfter(moment())) return session.send('对方的花园正在庇护之下喵~\n庇护结束时间：' + defUser[0].data.protectEndTime)
        // 确认对战信息
        let img = await renderBattleConfirm(ctx, defUser[0].plant, atkZombieList)
        await session.send(img)
        let text = '确认进攻此花园吗？\n确认请输入任意字符开始喵~\n取消进攻请输入「 取消 」'
        await session.send(text)
        const waitText = await session.prompt()
        if (waitText == '取消' || !waitText) return await session.send('已取消进攻喵~')
        else {
          text = '僵尸们缓缓向着' + defInfo.name + '的花园前进....'
          await session.send(text)
          defUser[0].data.isBattling = true // 标记战斗中
          await ctx.database.set('userList', defUser[0].id, {
            data: defUser[0].data
          })
          handleBattle(atkInfo, defInfo, false, false, atkZombieList, data)
          .then(async (res: any) => {
            defUser[0].data.isBattling = false
            defUser[0].data.sunTotal -= res.lostSun
            if (defUser[0].data.sunTotal < 0) defUser[0].data.sunTotal = 0
            if (res.lostPlant && res.lostPlant.length) { // 存在损失的植物 存入日志
              let reducePlant = res.lostPlant.reduce((pre, j) => {
                let hasIndex = pre.findIndex(i => i.id == j.id)
                if (hasIndex > -1) pre[hasIndex].num++
                else {
                  j.num = 1
                  pre.push(j)
                }
                return pre
              }, [])
              await saveLog(ctx, defUser[0].userLog, defUser[0].id, 'plantLog', {
                time: moment().format('YYYY-MM-DD HH:mm:ss'),
                addPlant: [], // 新增的植物
                reducePlant, // 减少的植物
                type: 'death',
                desc: atkInfo.name + '进攻了你的花园',
              })
            }
            defUser[0].data.protectEndTime = res.protectEndTime
            // 计算当前阳光生产效率
            defUser[0].data = calcTargetUserSunDropAmount(defUser[0].data, res.userPlantList)
            ctx.database.set('userList', defUser[0].id, {
              data: defUser[0].data,
            })
            await render(ctx, res.result).then(img => {
              session.send(img)
            })
            let text = session.platform == 'kook' || res.timeLine.length < 200 ? res.timeLine.join('\n') : res.log.join('\n')
            await render(ctx, text).then(img => {
              session.send(img)
            })
            await handleUserTaskDone(atkInfo.id, 'everyDayTask', 8) // 完成每日任务 攻击一次花园
          })
        }
      }
      else return session.send('墓园中没有可用僵尸....\n请先使用指令「 唤醒 」为你的墓园添加僵尸喵~')
    }
    else return session.send('未持有安息乡的钥匙喵\n请先去商店购买以解锁更多功能喵~')
  }

  // 完成任务
  const handleUserTaskDone = async (targetUserId: any, type = 'everyDayTask', taskId: number) => {
    let user = await ctx.database.get('userList', targetUserId)
    const userInfo = user[0]
    let { userTask, hasChange = false } = handleTaskDone(userInfo.task, type, taskId)
    if (hasChange) await ctx.database.set('userList', targetUserId, {
      task: userTask
    })
  }

  // 显示任务列表
  const showTask = async (session: any, isAt = false) => {
    let { event } = session
    let { user } = event
    let hasUser = await ctx.database.get('userList', user.id)
    let targetUser = hasUser[0]
    const list = targetUser.task
    let { 
      result,
      userTask,
      isReward = false, // 是否收取奖励
      resultReward,
      resultRewardText,
    } = getTaskListText(list)
    if (resultRewardText) result = result + '\n' +  resultRewardText
    await render(ctx, result).then(img => {
      session.send(img)
    })
    if (isAt) return // @用户不收取奖励 仅显示任务面板
    if (isReward) { // 领取奖励 任务状态变更
      let setParams = {
        task: userTask
      } as any
      let dataChange = false
      resultReward.map(i => {
        if (i.type == 'exp') {
          dataChange = true
          targetUser.data.exp += i.num
        }
        if (i.type == 'money') {
          dataChange = true
          targetUser.data.money += i.num
        }
      })
      if (dataChange) setParams.data = targetUser.data
      await ctx.database.set('userList', user.id, setParams)
    }
  }

  // 试炼
  const showTrial = async (session: any, isAt = false) => {
    
  }

  // 图鉴
  const showBook = async (session: any, isAt = false) => {
    const list = Object.values(plantInfoList)
    let text = [
      '当前已开放种植的植物：\n',
    ]
    list.map((i: any) => {
      text.push(i.name)
      text.push('种植所需阳光' + i.price)
      if (!i.isCanPlant) text.push('暂时不能种猪')
    })
    await render(ctx, text.join('\n')).then(img => {
      session.send(img)
    })
  }

  // 随机图片
  const getImage = async (session: any, keyword = '') => {
    const filterTag = ['R-18', '萝莉', '丰满', '丫头片子', '巨乳', '屁股', '幼', '胖次', 'loli', '女孩子', '魅惑', '肛门', '阴', '腋', '内裤', '臀部', '裸', '汗', '真空', '更衣', '内衣', '极上乳房', '乳房', '开脚', '失禁', '乳沟']
    const params = {
      'r18': 0,
      'proxy': 'i.pixiv.re',
    } as any
    if (keyword) {
      const isTag = keyword.indexOf(' ') > -1 // keyword是否存在空格
      if (isTag) {
        const tag = keyword.split(' ')
        params.tag = tag
      }
      else params.keyword = keyword
    }
    await Request.getImage(ctx, params)
    .then(async (res) => {
      const data = res.data[0]
      let { pid, tags, urls, ext } = data
      tags = tags.filter((i: any) => filterTag.some(item => item.indexOf(i) > -1) == false)
      const url = urls.original
      const name = `img${moment().unix()}`
      await session.send('勇者爱丽丝！开始下载~')
      // 下载图片
      request(url).pipe(fs.createWriteStream(`buffer/${name}.${ext}`)).on('close', async function() {
        console.log('图片下载成功', url)
        // 压缩图片
        Sharp(`buffer/${name}.${ext}`)
        .jpeg({ quality: 40 })
        .toBuffer(async (err, buffer) => {
          if (err) {
            await session.send('爱丽丝过热了....')
            return console.log('下载图片失败', err)
          }
          const img = h.image(buffer, 'image/png')
          const imgLog = `pid:${pid}\ntags:${tags.join('、')}`
          console.log('发送图片', imgLog)
          await session.send(imgLog)
          return await session.send(img)
        })
      })
      // 清理历史图片
      fs.readdir('buffer', async (err, files) => {
        if (err) console.log('读取文件失败', err)
        files.map(i => {
          let time = i.split('img')[1].split('.')[0]
          if (moment().unix() - Number(time) > 600) {
            fs.unlink(`buffer/${i}`, (err) => {
              if (err) console.log('删除缓存图片失败', i)
              else console.log('删除缓存图片成功', i)
            })
          }
        })
      })
    })
    .catch(async (err) => {
      await session.send('爱丽丝找不到图片（倒')
      console.log('getImage', err)
    })
  }

  ctx.on('message', async (session: any) => {

    let { content, event } = session

    let isAt = false
    let targetUserId = session.event.user.id // 信息源用户id

    // 去除两端空格
    content = content.trim()

    // 处理@人
    if (content.indexOf(`<at id="`) > -1) {
      let userId = content.split(`<at id="`)[1].split(`"/>`)[0]
      event.user.id = userId // 将用户转化为@对象
      // 去除<at id="xxx" />
      content = content.replace(`<at id="${userId}"/>`, '')
      isAt = true
    }

    // 去除两端空格
    content = content.trim()

    // 兼容以/开头或以喵结尾
    if (content.indexOf('/') == 0) content = content.replace('/', '')
    if (content.indexOf('喵') == content.length - 1) content = content.replace('喵', '')

    let { user } = event

    // 删除所有用户
    if (content == '删除所有用户') {
      await session.send('确认删除所有用户？确认请输入yes')
      const isOK = await session.prompt()
      if (isOK == 'yes') {
        return session.send('您没有相关权限喵~')
        // deleteAllUser()
        // return session.send('已删除所有用户')
      }
      else return
    }

    // 重置用户
    if (content == '重置用户') {
      await session.send('确认重置该账号吗？确认请输入yes')
      const isOK = await session.prompt()
      if (isOK == 'yes') {
        if (isAt) return session.send('您没有相关权限喵~')
        resetUser(user.id)
        return session.send('已重置')
      }
    }

    // 作弊
    if (content == '作弊') {
      let text = '请输入指令：\n1. 增加阳光\n2. 增加金币'
      await render(ctx, text).then(img => {
        session.send(img)
      })
      const order = await session.prompt()
      if (order == '1' || order == '2') {
        cheat(order, session)
        return session.send('已执行')
      }
      else return
    }

    // 更新用户植物数据 开发用 调整植物数值后 更新到所有用户
    if (content == '更新用户植物数据') {
      if (isAt) return
      let updateKey = ['atk', 'atkActionText', 'atkRange', 'interval', 'range', 'location', 'url'] // 需要更新的字段
      let deleteKey = ['def']
      let userList = await ctx.database.get('userList', {})
      userList.map((i: userListType) => {
        i.plant = i.plant.map(plantItem => {
          let updatePlant = Object.values(plantInfoList).find((j: any) => j.id == plantItem.id) 
          updateKey.map(key => {
            if (updatePlant[key] != undefined) plantItem[key] = updatePlant[key]
          })
          deleteKey.map(key => delete plantItem[key])
          return plantItem
        })
        ctx.database.set('userList', i.id, {
          plant: i.plant
        })
      })
      return session.send('已更新')
    }

    // 将所有用户的植物重置为初始值 危险操作
    if (content == '重置用户植物数据') {
      if (isAt) return
      let userList = await ctx.database.get('userList', {})
      userList.map((i: userListType) => {
        i.plant = i.plant.map(plantItem => {
          let newPlant = Object.values(plantInfoList).find((j: any) => j.id == plantItem.id)
          return newPlant as any
        })
        ctx.database.set('userList', i.id, {
          plant: i.plant
        })
      })
      return session.send('已更新')
    }

    // 重置所有用户的墓碑的可用僵尸为初始值
    if (content == '重置墓碑僵尸') {
      if (isAt) return
      let userList = await ctx.database.get('userList', {})
      userList.map((i: userListType) => {
        if (i.tombstone && i.tombstone.zombie && i.tombstone.zombie.length) {
          i.tombstone.zombie = i.tombstone.zombie.map(zombie => {
            let item = zombieList.find(j => j.id == zombie.id)
            item = item ? item : zombie
            return item
          })
          ctx.database.set('userList', i.id, {
            tombstone: i.tombstone
          })
        }
      })
      return session.send('已更新')
    }

    // 重置所有用户的装备
    if (content == '重置用户装备') {
      if (isAt) return
      let userList = await ctx.database.get('userList', {})
      userList.map((i: userListType) => {
        ctx.database.set('userList', i.id, {
          equipment: defaultUserEquipment
        })
      })
      return session.send('已更新')
    }

    // 重置所有用户的任务
    if (content == '重置用户任务') {
      if (isAt) return
      let userList = await ctx.database.get('userList', {})
      userList.map((i: userListType) => {
        ctx.database.set('userList', i.id, {
          task: getUserDefaultTask()
        })
      })
      return session.send('已更新')
    }

    // 重置墓碑
    if (content == '重置墓碑') {
      if (isAt) return
      let userList = await ctx.database.get('userList', {})
      userList.map((i: userListType) => {
        ctx.database.set('userList', i.id, {
          tombstone: getUserDefaultTombstone()
        })
      })
      return session.send('已更新')
    }

    // 购买
    if (content.indexOf('buy') == 0 || content.indexOf('购买') == 0) {
      if (isAt) return
      // 检测是否注册
      await checkRegister(session)
      // 当前用户
      let hasUser = await ctx.database.get('userList', user.id)
      let targetUser = hasUser[0]
      let name = content.indexOf('buy') == 0 ? content.split('buy')[1] : content.split('购买')[1]
      // 去除空格
      name = name.replace(/\s*/g, "")
      let goods = getShopList(targetUser.data, targetUser.items, targetUser.extension)
      let allShop = []
      goods.map(i => {
        i.itemList.map(j => allShop.push(j))
      })
      let targetGoods = allShop.find(i => i.name == name || i.alias.find(j => j == name))
      if (!targetGoods) return
      let { itemId, price, isCanMultiple, canBatchBuy, shopType, name: goodsName, everyDayLimit } = targetGoods
      if (!isCanMultiple) { // 是否可拥有多个
        let isHas = targetUser.items.findIndex(i => i.id == itemId) > -1
        if (isHas) return session.send('此道具不可重复购买喵~')
      }
      let needPrice = 0
      let buyNum = 1
      if (canBatchBuy) { // 可批量购买
        // 等待用户输入数量
        session.send('请输入购买数量：')
        let num = await session.prompt()
        // 检测是否是阿拉伯数字
        let isNumber = /^[0-9]+$/
        if (isNumber.test(num)) {
          needPrice = price * num
          buyNum = num
        }
        else return
      }
      else { // 不可批量购买
        needPrice = price
      }
      // 判断是否超过该商品的可购买数量
      if (everyDayLimit > 0) {
        let todayBuyThatItemLog = targetUser.userLog.moneyLog.filter(i => i.type == 'buy' && i.buyItemId == itemId && moment(i.time, 'YYYY-MM-DD HH:mm:ss').format('YYYY-MM-DD') == moment().format('YYYY-MM-DD'))
        let todayBuyNumber = todayBuyThatItemLog.reduce((a, b) => a + b.buyNum, 0)
        let todayCanBuyLimit = everyDayLimit - todayBuyNumber
        if (todayCanBuyLimit < buyNum) return session.send('今天购买此道具的数量已达上限喵~')
      }
      // 判断金币是否足够
      if (targetUser.data.money < needPrice) return session.send('杂鱼~你的金钱不够喵')
      // 扩建类商品
      if (shopType == 'extension') {
        // extension
        let { isCanRepeatBuy, extensionId, extensionNumber } = targetGoods
        if (!isCanRepeatBuy) { // 不可重复购买 检测扩建记录
          let usedExtension = targetUser.extension.usedExtension // 已购买的 不可重复购买的扩建的id
          let purchased = usedExtension.findIndex(i => i == extensionId) > -1
          if (purchased) return session.send('此商品不可重复购买喵~')
          // 添加扩建记录
          targetUser.extension.usedExtension.push(extensionId)
        }
        // 扩建数量增加
        targetUser.extension.extensionNumber = targetUser.extension.extensionNumber + extensionNumber
        // 扣除金币
        targetUser.data.money = targetUser.data.money - needPrice
        // 金币变化 存入日志
        saveLog(ctx, targetUser.userLog, user.id, 'moneyLog', {
          time: moment().format('YYYY-MM-DD HH:mm:ss'),
          num: needPrice * -1,
          beforeMoney: targetUser.data.money + needPrice,
          afterMoney: targetUser.data.money,
          type: 'buy',
          buyNum,
          desc: '购买 「' + goodsName + '」' + (buyNum > 1 ? 'x ' + buyNum : ''),
        })
        // 更新数据
        await ctx.database.set('userList', user.id, {
          data: targetUser.data,
          extension: targetUser.extension
        })
        await handleUserTaskDone(user.id, 'everyDayTask', 4) // 完成每日任务 购买一次
      }
      else { // 普通商品
        // 扣除金币
        targetUser.data.money = targetUser.data.money - needPrice
        // 添加物品
        let hasItemIndex = targetUser.items.findIndex(i => i.id == itemId)
        if (hasItemIndex > -1) { // 存在该道具
          targetUser.items[hasItemIndex].num = targetUser.items[hasItemIndex].num + Number(buyNum)
        }
        else { // 不存在该道具
          let itemInfo = itemList.find(j => j.id == itemId)
          targetUser.items.push({
            id: itemId,
            num: Number(buyNum),
            info: itemInfo
          })
        }
        // 金币变化 存入日志
        saveLog(ctx, targetUser.userLog, user.id, 'moneyLog', {
          time: moment().format('YYYY-MM-DD HH:mm:ss'),
          num: needPrice * -1,
          beforeMoney: targetUser.data.money + needPrice,
          afterMoney: targetUser.data.money,
          type: 'buy',
          buyItemId: itemId,
          buyNum,
          desc: '购买 「' + goodsName + '」' + (Number(buyNum) > 1 ? 'x ' + buyNum : ''),
        })
        // 更新数据库
        await ctx.database.set('userList', user.id, {
          data: targetUser.data,
          items: targetUser.items
        })
        await handleUserTaskDone(user.id, 'everyDayTask', 4) // 完成每日任务 购买一次
      }
      // 返回购买成功
      let text = `购买成功！\n你购买了${buyNum == 1 ? '' : buyNum + '个'}${targetGoods.name}`
      await render(ctx, text).then(img => {
        session.send(img)
      })
      if (itemId == '29') { // 购买安息乡的钥匙 解锁墓碑相关玩法
        const _ = getUserDefaultTombstone()
        let data = { ..._ }
        data.isUnlock = true
        await ctx.database.set('userList', user.id, {
          tombstone: data
        })
        await render(ctx, tombstoneOpenTip.join('\n')).then(img => {
          session.send(img)
        })
      }
    }

    // 使用
    if (content.indexOf('use') == 0 || content.indexOf('使用') == 0) {
      if (isAt) return
      // 检测是否注册
      await checkRegister(session)
      // 当前用户
      let hasUser = await ctx.database.get('userList', user.id)
      let targetUser = hasUser[0]
      let name = content.indexOf('use') == 0 ? content.split('use')[1] : content.split('使用')[1]
      // 去除空格
      name = name.replace(/\s*/g, "")
      let targetItem = targetUser.items.find(i => i.info.name == name || i.info.alias && i.info.alias.find(j => j == name))
      if (!targetItem) {
        let has = itemList.find(i => i.name == name || i.alias && i.alias.find(j => j == name))
        if (has) return session.send('你的背包里没有这个喵~')
        else return
      }
      else {
        if (targetItem.info.isCanUse) {
          let userSuccess = false
          if (targetItem.info.type == 'giftBox') { // 礼盒型道具
            let prizePool = [] // 奖池
            targetItem.info.gift.map(i => {
              let addNum = i.num
              for (let index = 0; index < addNum; index++) {
                prizePool.push(i)
              }
            })
            let prize = prizePool[Math.floor(Math.random() * prizePool.length)]
            if (prize.type == 'item') {
              let prizeId = prize.id
              let targetPrize = itemList.find(i => i.id == prizeId)
              let hasPrizeItemIndex = targetUser.items.findIndex(i => i.id == targetPrize) // 用户道具列表存在该奖品
              if (hasPrizeItemIndex > -1) { // 存在该奖品
                targetUser.items[hasPrizeItemIndex].num = targetUser.items[hasPrizeItemIndex].num + 1
              }
              else { // 不存在该奖品
                targetUser.items.push({
                  id: prizeId,
                  num: 1,
                  info: targetPrize
                })
              }
              // 更新数据库
              await ctx.database.set('userList', user.id, {
                items: targetUser.items
              })
              session.send(`恭喜你获得[ ${targetPrize.name} ]喵~`)
              userSuccess = true
            }
            else if (prize.type == 'money') {
              targetUser.data.money = targetUser.data.money + prize.value
              // 更新数据库
              await ctx.database.set('userList', user.id, {
                data: targetUser.data
              })
              // 金币变化 存入日志
              saveLog(ctx, targetUser.userLog, user.id, 'moneyLog', {
                time: moment().format('YYYY-MM-DD HH:mm:ss'),
                num: prize.value,
                beforeMoney: targetUser.data.money - prize.value,
                afterMoney: targetUser.data.money,
                type: 'useUtils',
                desc: `使用了「${targetItem.info.name}」`
              })
              await session.send(`恭喜你获得「${prize.value}金币」喵~`)
              userSuccess = true
            }
          }
          else if (targetItem.info.type == 'utils') { // 普通消耗型道具
            let changeType = targetItem.info.changeType
            let targetItemUseCallback = itemList.find(j => j.id == targetItem.id).callback
            let useSuccessTip = ''
            if (changeType == 'data') { // 此道具作用于userData
              let result = targetItemUseCallback(targetUser.data)
              if (targetUser.data.sunTotal != result.data.sunTotal) { // 阳光发生变化 存入日志
                let changeNum = result.data.sunTotal - targetUser.data.sunTotal
                await saveLog(ctx, targetUser.userLog, user.id, 'sunLog', {
                  time: moment().format('YYYY-MM-DD HH:mm:ss'),
                  num: changeNum,
                  beforeSun: targetUser.data.sunTotal - changeNum,
                  afterSun: result.data.sunTotal,
                  type: 'useItem',
                  desc: '使用道具 「' + targetItem.info.name + '」',
                })
              }
              targetUser.data = result.data
              useSuccessTip = result.useSuccessTip
            }
            else if (changeType == 'plant') {
              let result = targetItemUseCallback(targetUser.plant)
              targetUser.plant = result.plant
              useSuccessTip = result.useSuccessTip
            }
            // 更新数据库
            await ctx.database.set('userList', user.id, {
              data: targetUser.data,
              items: targetUser.items,
              plant: targetUser.plant
            })
            session.send(useSuccessTip)
            userSuccess = true
          }
          else if (targetItem.info.type == 'weatherUtils') { // 改变天气型道具
            if (targetItem.info.weatherUtilsAction) {
              let callback = weatherUtilsActionList[targetItem.info.weatherUtilsAction]
              userSuccess = callback(session)
            }
          }
          if (!userSuccess) return
          let index = targetUser.items.findIndex(i => i.id == targetItem.id)
          targetUser.items[index].num -= 1
          targetUser.items = targetUser.items.filter(i => i.num > 0)
          await handleUserTaskDone(user.id, 'everyDayTask', 5) // 完成每日任务 使用一次道具
          // 更新数据库
          return await ctx.database.set('userList', user.id, {
            items: targetUser.items
          })
        }
        else return session.send('此道具不可使用喵~')
      }
    }

    // 铲除
    if (content.indexOf('铲') == 0 && content.indexOf('个') >= 1) { // 支持铲个、铲3个、铲五个
      if (isAt) return
      // 检测是否注册
      await checkRegister(session)
      // 当前用户
      let hasUser = await ctx.database.get('userList', user.id)
      let targetUser = hasUser[0]
      if (targetUser.data.isBattling) return session.send('你的花园正在被入侵中喵~\n等结束了再铲吧！')
      // 处理铲除逻辑
      let plantName = content.split('铲')[1].split('个')[1]
      let plantNum = content.indexOf('个') == 1 ? 1 : content.split('铲')[1].split('个')[0]
      // 识别中文数字 转义为阿拉伯数字
      let isChineseNum = /^[\u4e00-\u9fa5]+$/
      plantNum = isChineseNum.test(plantNum) ? chineseNumberToNumber(plantNum) : plantNum
      let userPlantList = targetUser.plant
      let plant = ''
      for (const key in plantOrderList) {
        if (Object.prototype.hasOwnProperty.call(plantOrderList, key)) {
          const plantOrder = plantOrderList[key]
          let has = plantOrder.findIndex((i: String) => i == plantName) > -1
          if (has) {
            plant = key 
          }
        }
      }
      if (plant) {
        let deletePlantInfo = plantInfoList[plant]
        let hasThatPlantNumber = userPlantList.reduce((pre, cur) => pre + (cur.id == deletePlantInfo.id ? 1 : 0), 0)
        if (hasThatPlantNumber >= plantNum) {
          let userShovel = targetUser.equipment.shovel
          if (userShovel.itemId) {
            // 铲除逻辑 移除相关植物
            userPlantList = deletePlantCallback(userPlantList, targetUser.userConfig, plantNum, deletePlantInfo.id)
            // 铲除的植物 种植所需阳光
            let plantPrice = deletePlantInfo.price * plantNum
            // 根据装备的铲子 判断是否返还对应的阳光
            let hasReturnProbability = userShovel.info.returnProbability // 返还概率 0 - 1
            let hasReturn = Math.random() < hasReturnProbability
            let returnSun = 0
            if (hasReturn) {
              let returnRatio = userShovel.info.returnRatio // 返还比例 0 - 1
              returnSun = Math.floor(plantPrice * returnRatio)
              targetUser.data.sunTotal += returnSun
              saveLog(ctx, targetUser.userLog, user.id, 'sunLog', {
                time: moment().format('YYYY-MM-DD HH:mm:ss'),
                num: returnSun,
                beforeSun: targetUser.data.sunTotal - returnSun,
                afterSun: targetUser.data.sunTotal,
                type: 'return',
                desc: '铲除植物，高阶铲子概率返还阳光',
              })
            }
            // 重新计算当前阳光生产效率
            targetUser.data = calcTargetUserSunDropAmount(targetUser.data, userPlantList)
            // 更新数据库
            await ctx.database.set('userList', user.id, {
              data: targetUser.data,
              plant: userPlantList
            })
            await saveLog(ctx, targetUser.userLog, user.id, 'plantLog', {
              time: moment().format('YYYY-MM-DD HH:mm:ss'),
              addPlant: [], // 新增的植物
              reducePlant: [{ // 减少的植物
                name: deletePlantInfo.name,
                id: deletePlantInfo.id,
                num: plantNum
              }],
              type: 'delete',
              desc: '铲除',
            })
            await handleUserTaskDone(user.id, 'everyDayTask', 3) // 完成每日任务 铲除一棵植物
            let text = `已铲除${plantNum}个${deletePlantInfo.name}\n${returnSun ? '返还了' + returnSun + '阳光喵~' : '没有返还阳光喵~'}`
            return session.send(text)
          }
          else return session.send('你还没有装备铲子喵~')

        }
        else if (hasThatPlantNumber < plantNum && hasThatPlantNumber != 0) return session.send(`你没有${plantNum}个${deletePlantInfo.name}喵~`)
        else return session.send('你没有种植这个植物喵~')
      }
      else return
    }

    // 种植
    if (content.indexOf('种') == 0 && content.indexOf('个') >= 1) { // 支持种个、种3个、种五个
      if (isAt) return
      // 检测是否注册
      await checkRegister(session)
      // 当前用户
      let hasUser = await ctx.database.get('userList', user.id)
      let targetUser = hasUser[0]
      if (targetUser.data.isBattling) return session.send('你的花园正在被入侵中喵~\n等结束了再种吧！')
      // 处理种植逻辑
      let plantName = content.split('种')[1].split('个')[1]
      let plantNum = content.indexOf('个') == 1 ? 1 : content.split('种')[1].split('个')[0]
      // 识别中文数字 转义为阿拉伯数字
      let isChineseNum = /^[\u4e00-\u9fa5]+$/
      plantNum = isChineseNum.test(plantNum) ? chineseNumberToNumber(plantNum) : plantNum
      let userPlantList = targetUser.plant
      if (plantNum > (targetUser.data.maxPlants + targetUser.extension.extensionNumber) - userPlantList.length) return session.send('你的植物上限不足！')
      let plant = ''
      for (const key in plantOrderList) {
        if (Object.prototype.hasOwnProperty.call(plantOrderList, key)) {
          const plantOrder = plantOrderList[key]
          let has = plantOrder.findIndex((i: String) => i == plantName) > -1
          if (has) {
            plant = key 
          }
        }
      }
      if (plant) {
        let targetPlant = plantInfoList[plant]
        if (!targetPlant.isCanPlant) return session.send('该植物暂未开放种植，等待后续更新喵~')
        // 判断阳光是否足够
        let needSun = targetPlant.price * plantNum
        if (targetUser.data.sunTotal < needSun) {
          let error = `种植所需阳光：${needSun}\n当前可用阳光：${targetUser.data.sunTotal}\n杂鱼~你的阳光不够啦，再多等一会喵！\n或许收一下阳光就够了~`
          render(ctx, error).then(img => {
            session.send(img)
          })
          return
        }
        // 判断是否在种植的cd内
        else if (targetPlant.cd) { // 该植物存在种植cd
          if (plantNum > 1) return session.send('该植物存在种植冷却...\n一次性只能种植一株喵~')
          let hasCd = targetUser.userLog.plantLog.filter(log => log.type == 'plant' && moment().isBefore(moment(log.time, 'YYYY-MM-DD HH:mm:ss').add(targetPlant.cd, 'minutes')) && log.addPlant.find(j => j.id == targetPlant.id))
          if (hasCd.length) return session.send(`该植物正在冷却中喵~\n植物冷却时长：${targetPlant.cd}分钟\n冷却完成时间：` + moment(hasCd[0].time, 'YYYY-MM-DD HH:mm:ss').add(targetPlant.cd, 'minutes').format('YYYY-MM-DD HH:mm:ss'))
        }
        if (targetPlant.isGrow) { // 成长型植物
          targetPlant.growEndTime = moment().add(targetPlant.growRemainingTime, 'hours').format('YYYY-MM-DD HH:mm:ss') // 成长完成时间
        }
        for (let index = 0; index < plantNum; index++) { // 存入植物
          userPlantList.push(targetPlant)
        }
        targetUser.data.sunTotal = targetUser.data.sunTotal - needSun
        await saveLog(ctx, targetUser.userLog, user.id, 'sunLog', {
          time: moment().format('YYYY-MM-DD HH:mm:ss'),
          num: needSun * -1,
          beforeSun: targetUser.data.sunTotal + needSun,
          afterSun: targetUser.data.sunTotal,
          type: 'plant',
          desc: '种植 ' + targetPlant.name + ' x ' + plantNum,
        })
        await saveLog(ctx, targetUser.userLog, user.id, 'plantLog', {
          time: moment().format('YYYY-MM-DD HH:mm:ss'),
          addPlant: [{ // 新增的植物
            name: targetPlant.name,
            id: targetPlant.id,
            num: plantNum,
          }],
          reducePlant: [], // 减少的植物
          type: 'plant',
          desc: '种植',
        })
        // 计算当前阳光生产效率
        targetUser.data = calcTargetUserSunDropAmount(targetUser.data, userPlantList)
        await ctx.database.set('userList', user.id, {
          plant: userPlantList,
          data: targetUser.data
        })
        await handleUserTaskDone(user.id, 'everyDayTask', 2) // 完成每日任务 种值一棵植物
        let url = pathToFileURL(resolve(__dirname, `img/plant/${targetPlant.img}.gif`)).href
        return await session.send(h.image(url))
      }
      else return
    }

    // 图鉴
    if (content.indexOf('图鉴') == 0 && content.length > 2) {
      if (isAt) return
      // 检测是否注册
      await checkRegister(session)
      let name = content.split('图鉴')[1]
      name = name.replace(/\s*/g, "") // 去除空格
      let plant = ''
      for (const key in plantOrderList) {
        if (Object.prototype.hasOwnProperty.call(plantOrderList, key)) {
          const plantOrder = plantOrderList[key]
          let has = plantOrder.findIndex((i: String) => i == name) > -1
          if (has) {
            plant = key 
          }
        }
      }
      if (plant) {
        const targetPlant = plantInfoList[plant]
        if (!targetPlant.isCanPlant) return session.send('该植物暂未开放种植，等待后续更新喵~')
        await renderPlantInfo(ctx, targetPlant).then(img => {
          session.send(img)
        })
      }
      else return session.send('没有这个植物喵~')
    }

    if (content.indexOf('装备') == 0 && content.length > 2) {
      if (isAt) return
      // 检测是否注册
      await checkRegister(session)
      // 当前用户
      let hasUser = await ctx.database.get('userList', user.id)
      let targetUser = hasUser[0]
      // 处理装备逻辑
      let equipName = content.split('装备')[1]
      equipName = equipName.replace(/\s*/g, "")
      if (equipName) {
        let userEquipment = targetUser.equipment
        let equipmentItems = targetUser.items.filter(i => i.info.type == 'equipment' || i.info.type == 'shovel' || i.info.type == 'sunReservoir')
        if (equipmentItems.length) {
          let useItem = equipmentItems.find(i => i.info.name == equipName || (i.info.alias && i.info.alias.findIndex(a => a == equipName) > -1))
          if (!useItem) return session.send('你的背包里没有这个装备喵~')
          else {
            if (useItem.info.type == 'shovel') { // 装备铲子
              if (userEquipment.shovel.info.name == equipName) return session.send('你已经装备了这个铲子喵~')
              else {
                targetUser.equipment.shovel.itemId = useItem.id
                targetUser.equipment.shovel.info = useItem.info
                // 更新数据库
                await ctx.database.set('userList', user.id, {
                  equipment: targetUser.equipment
                })
                return session.send(`成功装备铲子：${useItem.info.name}`)
              }
            }
            else if (useItem.info.type == 'sunReservoir') { // 装备阳光储存器
              if (userEquipment.sunReservoir.info.name == equipName) return session.send('你已经装备了这个阳光储存器喵~')
              else if (userEquipment.sunReservoir.info.name) return session.send('你已经装备了阳光储存器喵~\n当前储存器损坏后才能装备新的储存器喵~')
              else {
                targetUser.equipment.sunReservoir.itemId = useItem.id
                targetUser.equipment.sunReservoir.info = useItem.info
                // 更新数据库
                await ctx.database.set('userList', user.id, {
                  equipment: targetUser.equipment
                })
                return session.send(`成功装备阳光储存器：${useItem.info.name}\n阳光的储存上限提升了喵！`)
              }
            }
            else if (equipName == 'plantBook') return session.send('植物图鉴在商店购买后会自动装备喵~')
            else if (equipName == 'botany') return session.send('《植物学》在商店购买后会自动装备喵~')
            else return session.send('这是装备了什么....本喵不认识~')
          }
        }
        else return session.send('你的背包里没有装备喵~')
      }
    }

    // 关键词 允许后方添加参数 非简易指令
    let keyword = {
      battlelog: ['战斗日志', '战斗记录', 'battlelog', 'battlel', 'blog', 'bl'],
      battleinfo: ['战斗信息', 'battleinfo', 'battlei', 'binfo', 'bi'],
      atk: ['攻击', '进攻', 'atk'],
      getImage: ['图片', '搜图'],
      groupTotal: ['群统计', 'ba统计'],
    }
    let hasKeyWork = '' // 指令识别 用于判断需要触发的函数
    let hasKeyWorkParams = '' // 指令后续参数
    let actionKeyWork = '' // 触发的文字
    for (const key in keyword) {
      if (Object.prototype.hasOwnProperty.call(keyword, key)) {
        const item = keyword[key]
        // 支持以/为前缀 或不加前缀
        let has = item.findIndex((i: string) => {
          let isHas = content.indexOf(i) == 0 || content.indexOf('/' + i) == 0
          if (isHas) actionKeyWork = i
          return isHas
        }) > -1
        if (has) {
          hasKeyWork = key
          hasKeyWorkParams = content.split(actionKeyWork)[1]
          // 去除两端空格
          hasKeyWorkParams = hasKeyWorkParams.replace(/^\s*/g, "").replace(/\s*$/g, "")
        }
      }
    }
    if (hasKeyWork == 'battlelog') {
      await checkRegister(session) // 检测是否注册
      if (hasKeyWorkParams) {
        if (/^\d+$/.test(hasKeyWorkParams)) {
          hasKeyWorkParams = Number(hasKeyWorkParams) as any
          showBattleLog(session, hasKeyWorkParams as any)
        }
        else return session.send('请在指令后方跟随查询的页数喵~')
      }
      else showBattleLog(session)
    }
    else if (hasKeyWork == 'battleinfo') {
      await checkRegister(session) // 检测是否注册
      if (hasKeyWorkParams) {
        if (/^\d+$/.test(hasKeyWorkParams)) {
          hasKeyWorkParams = Number(hasKeyWorkParams) as any
          showBattleInfo(session, hasKeyWorkParams as any)
        }
        else return session.send('请在指令后方跟随查询的对局id喵~')
      }
      else return session.send('请在指令后方跟随查询的对局id喵~')
    }
    else if (hasKeyWork == 'atk') {
      let atkUserId = '' // 进攻方userId
      let defUserId = '' // 防守方userId
      if (isAt) {
        atkUserId = targetUserId
        let list = await ctx.database.get('userList', user.id)
        if (list.length == 0) return session.send('他还没入驻花园喵~')
        else defUserId = user.id
        session.event.user.id = targetUserId // 识别用户id后 将会话id恢复
        await atkUser(session, atkUserId, defUserId)
      }
      else if (hasKeyWorkParams) {
        atkUserId = user.id
        let list = await ctx.database.get('userList', {})
        if (hasKeyWorkParams.indexOf('#') > -1) {
          let userName = hasKeyWorkParams.split('#')[1]
          let index = list.findIndex((i: userListType) => i.name.indexOf(userName) > -1) // 模糊搜索
          if (index > -1) {
            defUserId = list[index].id
            await atkUser(session, atkUserId, defUserId)
          }
          else return session.send('找不到该用户喵~\n可能是用户更改了群名称或没有入驻花园\n可以试试攻击@xxxx')
        }
        else {
          let index = list.findIndex((i: userListType) => i.name.indexOf(hasKeyWorkParams) > -1)
          if (index > -1) {
            defUserId = list[index].id
            await atkUser(session, atkUserId, defUserId)
          }
          else return session.send('找不到该用户喵~\n可能是用户更改了群名称或没有入驻花园\n可以试试攻击@xxxx')
        }
      }
      else return session.send('请在指令后方跟随想要攻击的花园喵~\n支持@和#模糊名称搜索\n例如: 攻击@xxx || 攻击#xxx')
    }
    else if (hasKeyWork == 'getImage') {
      let keyword = ''
      if (hasKeyWorkParams) {
        keyword = hasKeyWorkParams
      }
      getImage(session, keyword)
    }

    // 遍历简易指令
    let order = '' // 触发的指令
    for (const key in simpleOrderList) {
      if (Object.prototype.hasOwnProperty.call(simpleOrderList, key)) {
        const item = simpleOrderList[key]
        // 支持以/为前缀 或不加前缀
        let has = item.findIndex((i: String) => i == content || '/' + i == content) > -1
        if (has) {
          order = key 
        }
      }
    }
    if (order == 'plant') return myPlant(session)
    else if (order == 'info') return userInfo(session)
    else if (order == 'harvest' && !isAt) return harvestSun(session)
    else if (order == 'shop') return showShop(session)
    else if (order == 'allShop') return showShop(session, true)
    else if (order == 'sign' && !isAt) return signIn(session)
    else if (order == 'items') return showItems(session)
    else if (order == 'equip') return showEquipment(session)
    else if (order == 'level') return showLevel(session)
    else if (order == 'log') return showLog(session, 'all')
    else if (order == 'sunLog') return showLog(session, 'sunLog')
    else if (order == 'moneyLog') return showLog(session, 'moneyLog')
    else if (order == 'plantLog') return showLog(session, 'plantLog')
    else if (order == 'resetLog') return resetLog(session)
    else if (order == 'weather') return showWeather(session)
    else if (order == 'strategy') return showUserStrategy(session)
    else if (order == 'fakeBattle') return fakeBattle(session, isAt)
    else if (order == 'help') return showHelp(session)
    else if (order == 'orderHelp') return showOrderHelp(session)
    else if (order == 'ranking') return showRanking(session)
    else if (order == 'tombstone') return showTombstone(session)
    else if (order == 'wakeup' && !isAt) return wakeUpZombie(session, isAt)
    else if (order == 'task') return showTask(session, isAt)
    else if (order == 'trial') return showTrial(session, isAt)
    else if (order == 'book') return showBook(session)
    else return

  })

  
  // 0点刷新用户数据
  const TimerFotRefreshUserData = async () => {
    // 重置全体用户签到状态
    let userList = await ctx.database.get('userList', {})
    userList = userList.map(i => {
      i.data.targetDateIsSignIn = false
      i.task.everyDayTask = everyDayTask
      return i
    })
    userList.map((i: userListType) => {
      ctx.database.set('userList', [i.id], {
        data: i.data,
        task: i.task,
      })
    })
  }

  // 更新所有用户当前阳光数量 注册于分钟定时器
  const TimerFotHarvestSun = async (userList: Array<userListType>) => {
    // console.log('定时生产阳光');
    let weather = await ctx.database.get('weather', {})
    let targetWeather = weather[0]
    userList = userList.map((i) => {
      i.data = userHarvestSunCallback(i.data, targetWeather, i.plant, i.equipment)
      i.tombstone = userWakeUpZombieCallback(i.tombstone)
      // i.data.isChanged = true
      return i
    })
    userList.map((i: userListType) => {
      if (i.data.isChanged) { // 用户信息发生变化
        delete i.data.isChanged
        if (i.data.isChangedMoney && i.data.addMoney) { // 用户金钱发生变化 金盏花等生产金钱的植物专属
          delete i.data.isChangedMoney
          // 金币变化 存入日志
          saveLog(ctx, i.userLog, i.id, 'moneyLog', {
            time: moment().format('YYYY-MM-DD HH:mm:ss'),
            num: i.data.addMoney,
            beforeMoney: i.data.money - i.data.addMoney,
            afterMoney: i.data.money,
            type: 'harvest',
            desc: '植物生产金钱',
          })
          delete i.data.addMoney
        }
        // console.log('用户信息更改', i);
        ctx.database.set('userList', [i.id], {
          data: i.data,
          // userLog: defaultUserLog,
          // userConfig: defaultUserConfig,
          // task: getUserDefaultTask(),
          // extension: defaultUserExtension,
          // equipment: defaultUserEquipment,
          // tombstone: getUserDefaultTombstone(),
        })
      }
      if (i.tombstone.isChanged) {
        delete i.tombstone.isChanged
        ctx.database.set('userList', [i.id], {
          tombstone: i.tombstone,
        })   
      }
    })
  }

  // 更新所有用户已种植的 可成长植物
  const TimerForGrowPlant = async (userList: Array<userListType>) => {
    userList.map(i => {
      const { plant = i.plant, isChanged = false } = userGrowPlantCallback(i.plant)
      if (isChanged) {
        ctx.database.set('userList', [i.id], {
          plant
        })
      }
    })
  }

  // 运行项目时初始化天气
  const initWeather = async () => {
    let weather = await ctx.database.get('weather', {})
    let targetWeather = weather[0]
    if (!targetWeather) {
      await changeWeather(false)
    }
    else {
      let hour = moment().hour()
      let weatherHour = moment(targetWeather.time, 'YYYY/MM/DD HH:mm:ss').hour()
      if (hour != weatherHour) {
        await changeWeather(false)
      }
    }
  }

  // 0点 同步更新所有群员信息
  const TimerFotRefreshGuildMemberInfo = async () => {
    let bot = ctx.bots[0]
    bot.getGuildList()
    .then(res => {
      let result = []
      res.data.map(i => { // id去重
        if (result.length == 0 || result.find(item => item.id == i.id)) result.push(i)
      })
      result.map(i => {
        bot.getGuildMemberList(i.id)
        .then(list => {
          console.log('getGuildMemberList list', list);
        })
        .catch(err => console.log('获取群员信息失败', err))
      })
    })
    .catch(err => console.log('获取已加入的群聊失败', err))
  }

  // 野怪生成 每10分钟生成一波野怪 随机对未处于庇护状态的用户发起攻击
  const TimerFotZombieGenerate = async (type = 'normal', data: any) => {
    let userList : Array<userListType> = await ctx.database.get('userList', {})
    userList = userList.filter((i: userListType) => {
      // 不在庇护状态下 且 不在战斗状态中
      return (i.data.protectEndTime == '' || moment(i.data.protectEndTime, 'YYYY-MM-DD HH:mm:ss').isBefore(moment())) && !i.data.isBattling
    })
    // console.log('定时生成野怪', userList);
    if (userList.length == 0) {
      if (type == 'excrescentZombie') return data.session.send('附近的花园都处于庇护状态中，游荡的野怪远去了....')
    }
    // 随机一位
    let user = userList[Math.floor(Math.random() * userList.length)]
    if (user) {
      // sendGuildMessage(`腐烂的气息正在蔓延....\n一大波游荡的僵尸正在逼近${user.name}的花园喵~`)
      let atkInfo = {
        name: '游荡的僵尸',
        id: '0'
      }
      let defInfo = {
        name: user.name,
        id: user.id,
        plant: user.plant
      }
      user.data.isBattling = true
      await ctx.database.set('userList', user.id, {
        data: user.data
      })
      // console.log('野怪生成', atkInfo, defInfo);
      handleBattle(atkInfo, defInfo, false, false, data?.excrescentZombie || false, { sunTotal: user.data.sunTotal })
      .then(async (res: any) => {
        user.data.isBattling = false
        user.data.sunTotal -= res.lostSun
        if (user.data.sunTotal < 0) user.data.sunTotal = 0
        if (res.lostPlant && res.lostPlant.length) { // 存在损失的植物 存入日志
          let reducePlant = res.lostPlant.reduce((pre, j) => {
            let hasIndex = pre.findIndex(i => i.id == j.id)
            if (hasIndex > -1) pre[hasIndex].num++
            else {
              j.num = 1
              pre.push(j)
            }
            return pre
          }, [])
          await saveLog(ctx, user.userLog, user.id, 'plantLog', {
            time: moment().format('YYYY-MM-DD HH:mm:ss'),
            addPlant: [], // 新增的植物
            reducePlant, // 减少的植物
            type: 'death',
            desc: '游荡的僵尸来袭',
          })
        }
        user.data.protectEndTime = res.protectEndTime
        // 计算当前阳光生产效率
        user.data = calcTargetUserSunDropAmount(user.data, res.userPlantList)
        ctx.database.set('userList', user.id, {
          data: user.data,
        })
        if (res.lostSun == 0 && res.lostPlant.length == 0) {
          await handleUserTaskDone(user.id, 'everyDayTask', 5) // 完成每日任务 受到野怪攻击且没有损失
        }
        // await render(ctx, res.result).then(img => {
        //   // sendGuildMessage(img)
        // })
        // let text = res.timeLine.length > 250 ? res.log.join('\n') : res.timeLine.join('\n')
        // console.log('野怪 结束', text)
        // await render(ctx, text).then(img => {
        //   sendGuildMessage(img)
        // })
      })
    }
  }

  // 初始化用户数据 剔除异常数据
  const initUserData = async () => {
    // 无效字段
    let userDataDisableKey = ['isChanged', 'isChangedMoney', 'addMoney']
    let userList = await ctx.database.get('userList', {})
    userList = userList.map((i: userListType) => {
      // 战斗状态重置
      if (i.data.isBattling) {
        i.data.isBattling = false
        i.data.isChanged = true // 需要更新数据库
      }
      let userSunDropAmount = calcTargetUserSunDropAmount(i.data, i.plant).sunDropAmount
      // 阳光生产量纠正
      if (userSunDropAmount != i.data.sunDropAmount) {
        i.data.sunDropAmount = userSunDropAmount
        i.data.isChanged = true // 需要更新数据库
      }
      // 剔除无效字段
      let keys = Object.keys(i.data)
      keys.map(key => {
        if (userDataDisableKey.find(item => item == key)) {
          delete i.data[key]
          i.data.isChanged = true // 需要更新数据库
        }
      })
      return i
    })
    userList.map((i: userListType) => {
      if (i.data.isChanged) {
        delete i.data.isChanged
        ctx.database.set('userList', [i.id], {
          data: i.data
        })
      }
    })
  }

  // ----------------------------
  // 以下为初始化部分
  // 初始化天气
  initWeather().then(async () => {
    // 立即执行一遍定时器逻辑
    const userList = await ctx.database.get('userList', {})
    TimerFotHarvestSun(userList)
    TimerForGrowPlant(userList)
  })
  initUserData()
  // 初始化结束
  // ----------------------------


  // ----------------------------
  // 以下为定时器部分
  // 分钟定时器
  ctx.setInterval(async () => {
    // 整点切换天气
    let isNewHour = moment().minute() == 0
    if (isNewHour) {
      await changeWeather(true, false, false, false)
    }
    // 6点、18点 日夜交界处 重置计算用户阳光生产效率
    let isDayNightChange = moment().minute() == 0 && (moment().hour() == 6 || moment().hour() == 18)
    if (isDayNightChange) initUserData()
    // 每分钟更新用户数据
    const userList = await ctx.database.get('userList', {})
    await TimerFotHarvestSun(userList)
    await TimerForGrowPlant(userList)
    // 0点 刷新用户数据 获取群员信息
    let isNewDate = moment().hour() == 0 && moment().minute() == 0
    if (isNewDate) { // 0点更新用户数据
      TimerFotRefreshUserData()
      TimerFotRefreshGuildMemberInfo()
    }
    // 每10分钟触发 野怪刷新
    if (moment().minute() % 10 == 0) {
      if (gameConfig.stopSpawnZombies) return // 停止刷新野怪
      TimerFotZombieGenerate('normal', {})
    }
  }, 60000)
  // 定时器结束
  // ----------------------------

  ctx.on('interaction/button', (res) => {
    console.log('点击了按钮', res);
  })

}

async function render(ctx: Context, content: string, isLongText: boolean = false) {
  let res = await ctx.puppeteer.render(`<html>
    <head>
      <style>
        @font-face {
          font-family: AlibabaPuHuiTi-2-55-Regular;
          src:url(https://puhuiti.oss-cn-hangzhou.aliyuncs.com/AlibabaPuHuiTi-2/AlibabaPuHuiTi-2-55-Regular/AlibabaPuHuiTi-2-55-Regular.woff2) format('woff2');
        }
        html {
          font-family: 'AlibabaPuHuiTi-2-55-Regular', 'Microsoft YaHei', 'Noto Sans SC', sans-serif;
          width: max-content;
          height: 0;
          background: white;
          max-width: 600px;
          min-width: 200px;
          background-image: url(https://pic.imgdb.cn/item/66656e745e6d1bfa05aee62b.jpg);
          background-size: 120px 120px;
          ${isLongText && 'transform: scale(0.6);'}
        }
        p {
          padding: 10px;
          word-wrap: break-word;
          white-space: pre-wrap;
          font-size: 12px;
          color: #eee;
        }
      </style>
    </head>
    <body>
      <p>${content}</p>
    </body>
    </html>`);
  return res
}

async function renderShop(ctx: Context, goods: Array<any>, userData: defaultUserInfoType, showAll: boolean = false, userLog: UserLog) {
  return await ctx.puppeteer.render(renderGoodsHTML(goods, userData, showAll, userLog))
}

async function renderUserItems(ctx: Context, items: Array<any>, userData: defaultUserInfoType) {
  return await ctx.puppeteer.render(renderItemsHTML(items, userData))
}

async function renderUserEquipment(ctx: Context, equipments: UserEquipment) {
  return await ctx.puppeteer.render(renderEquipmentHTML(equipments))
}

async function renderUserLevel(ctx: Context, userData: defaultUserInfoType) {
  return await ctx.puppeteer.render(renderUserLevelHTML(userData))
}

async function renderUserLog(ctx: Context, userLog: UserLog, type: 'plantLog'| 'moneyLog' | 'sunLog') {
  return await ctx.puppeteer.render(renderUserLogHTML(userLog, type))
}

async function renderRanking(ctx: Context, list: any) {
  return await ctx.puppeteer.render(renderRankingHTML(list))
}

async function renderTombstone(ctx: Context, tombstone: any) {
  return await ctx.puppeteer.render(renderTombstoneHTML(tombstone))
}

async function renderBattleLog(ctx: Context, battleLog: Array<Battle>) {
  return await ctx.puppeteer.render(renderBattleLogHTML(battleLog))
}

async function renderBattleConfirm(ctx: Context, defUserPlant: Array<plant>, zombie: Array<any>) {
  return await ctx.puppeteer.render(renderBattleConfirmHTML(defUserPlant, zombie))
}

async function renderPlantInfo(ctx: Context, plant: plant) {
  return await ctx.puppeteer.render(renderPlantInfoHTML(plant))
}

// 保存日志
async function saveLog(ctx: Context, userLog: UserLog, userId: number | string, type: 'plantLog'| 'moneyLog' | 'sunLog', logData: any) {
  userLog[type].push(logData)
  await ctx.database.set('userList', [userId], {
    userLog
  })
}